package checker

func (c *checker) createUnaryTypeMapper(source Type, target Type) *TypeMapper {
	return &TypeMapper{Invoke: func(t Type) Type {
		if t == source {
			return target
		} else {
			return t
		}
	}}
}

func (c *checker) createBinaryTypeMapper(source1 Type, target1 Type, source2 Type, target2 Type) *TypeMapper {
	return &TypeMapper{Invoke: func(t Type) Type {
		if t == source1 {
			return target1
		} else if t == source2 {
			return target2
		} else {
			return t
		}
	}}
}

func (c *checker) createTypeMapper(sources []Type, targets []Type) *TypeMapper {
	switch len(sources) {
	case 1:
		return c.createUnaryTypeMapper(sources[0], targets[0])
	case 2:
		return c.createBinaryTypeMapper(sources[0], targets[0], sources[1], targets[1])
	}
	return &TypeMapper{Invoke: func(t Type) Type {
		for i := 0; i < len(sources); i++ {
			if t == sources[i] {
				return targets[i]
			}
		}
		return t
	}}
}

func (c *checker) createUnaryTypeEraser(source Type) *TypeMapper {
	return &TypeMapper{Invoke: func(t Type) Type {
		if t == source {
			return c.globalObjectType
		}
		return t
	}}
}

func (c *checker) createBinaryTypeEraser(source1 Type, source2 Type) *TypeMapper {
	return &TypeMapper{Invoke: func(t Type) Type {
		if t == source1 || t == source2 {
			return c.globalObjectType
		}
		return t
	}}
}

func (c *checker) createTypeEraser(sources []Type) *TypeMapper {
	switch len(sources) {
	case 1:
		return c.createUnaryTypeEraser(sources[0])
	case 2:
		return c.createBinaryTypeEraser(sources[0], sources[1])
	}
	return &TypeMapper{Invoke: func(t Type) Type {
		for i := 0; i < len(sources); i++ {
			if t == sources[i] {
				return c.globalObjectType
			}
		}
		return t
	}}
}

func (c *checker) createInferenceMapper(context *inferenceContext) *TypeMapper {
	return &TypeMapper{Invoke: func(t Type) Type {
		for i := 0; i < len(context.typeParameters); i++ {
			if t == context.typeParameters[i] {
				return context.getInferredType(i)
			}
		}
		return t
	}}
}

func (c *checker) combineTypeMappers(mapper1 *TypeMapper, mapper2 *TypeMapper) *TypeMapper {
	return &TypeMapper{Invoke: func(t Type) Type {
		return mapper2.Invoke(mapper1.Invoke(t))
	}}
}
